/*
 * SistemaDelTestSet.cpp
 *
 *  Created on: Dec 9, 2014
 *      Author: ale
 */

#include "SistemaDelTestSet.h"
#include <ctime>

SistemaDelTestSet::SistemaDelTestSet(const char* ARPAfile, const char* setPrueba,const char* archivoSalida) {
	heisenberg = new Predictor3();
	parserARPA = new ARPAParser(ARPAfile);
	parserTestSet = new ParserTestSet(setPrueba, archivoSalida);
	this->ARPAfile = ARPAfile;
	this->setPrueba = setPrueba;
	noprecide = 0;

}

SistemaDelTestSet::~SistemaDelTestSet() {
	// TODO Auto-generated destructor stub
}


Predictor3* SistemaDelTestSet::procesarARPAfile(){
	cout << "SOY EL PREDICTOR" << endl;

	parserARPA->cargarArchivo();
	parserARPA->cargarCantNGramas();
	cout << "Cant Unigramas: " << parserARPA->cantUnigramas << endl;
	cout << "Cant Bigramas: " <<  parserARPA->cantBigramas << endl;
	cout << "Cant Trigramas: " << parserARPA->cantTrigramas << endl;


	//heisenberg.bigramas->buscarBigrama("hola", "que");
	clock_t start;

	start = clock();
	parserARPA->cargarUniGramas(heisenberg);
	cout << "Time de iteracion: " << (std::clock() - start) / (double)(CLOCKS_PER_SEC / 1000) << " ms";
	cout << endl;

	start = clock();
	parserARPA->cargarBiGramas(heisenberg);
	cout << "Time de iteracion: " << (std::clock() - start) / (double)(CLOCKS_PER_SEC / 1000) << " ms";
	cout << endl;

	start = clock();
	parserARPA->cargarTriGramas(heisenberg);
	cout << "Time de iteracion: " << (std::clock() - start) / (double)(CLOCKS_PER_SEC / 1000) << " ms";
	cout << endl;
	return heisenberg;
}

void SistemaDelTestSet::procesarSetPrueba(){

	vector<string> *trigramas = new vector<string>();

	//parserTestSet->cargarArchivo();//si no esta cargado...

	//trigramas = parserTestSet->procesarSiguienteOracion(trigramas); //devuelve el vector con todos los trigramas de la frase

	trigramas = parserTestSet->procesarSiguienteOracionEnMemoria(trigramas);

	if(trigramas == NULL){
		parserTestSet->completarOracionConEnMemoria("",""); //nada
		return;
	}

	vector<double> probas(trigramas->size());

	for (size_t i=0;i<trigramas->size();i++){
		probas[i] = heisenberg->obtenerProb((*trigramas)[i]);
		//cout << (*trigramas)[i] << ": prob = " << probas[i];
	}
/*
	cout << endl;
	cout << endl;
	for (size_t i=0;i<trigramas->size();i++){
		cout <<"Trigrama: " << (*trigramas)[i];
		cout <<"    Probabilidad--> " << probas[i];
		cout << endl;
	}
*/
	string triConMenorProb = "";
	string palabraACompletar = "";
	int indexTrigramaConMenorProbabilidad;

	while(palabraACompletar == ""){
		indexTrigramaConMenorProbabilidad = trigramaMenosProbable(&probas);
		//cout << endl;
		//cout << "LA PALABRA VA ACA (entre la ultima palabra y la del medio------> ";
		//cout << (*trigramas)[indexTrigramaConMenorProbabilidad] << endl;

		if (indexTrigramaConMenorProbabilidad == -1){
			for(unsigned int i = 0; i< trigramas->size(); i++)
				cout << (*trigramas)[i] << endl;
			cout << "INDICE ES -1!!!"<<endl;
			parserTestSet->completarOracionCon("", "");
			delete trigramas;
			return;
		}
		triConMenorProb = (*trigramas)[indexTrigramaConMenorProbabilidad];


		palabraACompletar = buscarMejorTrigramaQueComplete(triConMenorProb); //busca la palabra que complete AB de ABC

		//Si no esta el trigrama, busca los bigramas.
		if(palabraACompletar == ""){
			palabraACompletar = buscarMejorBigramaQueComplete(triConMenorProb);//busca la palabra que complete B de BC (pero le paso ABC)
			//Si no esta el bigrama, busco otra parte de la oracion para completar.
			//me parece mejor que completar con el mejor unigrama que va a ser siempre el mismo.
			if(palabraACompletar == ""){
				//buscar otro trigrama para completar
				if (trigramas->size() > 1){
					trigramas->erase(trigramas->begin()+indexTrigramaConMenorProbabilidad);
					probas.erase(probas.begin()+indexTrigramaConMenorProbabilidad);
					//cout <<"NO HABIA NI TRIGRAMA NI BIGRAMA" <<endl;
				}
				else{
					palabraACompletar = "the"; //WTF jaja --> es la palabra mas usada del ingles
					noprecide+=1;
				}
			}
		}
	}

	//Completa la oracion con palabraACompletar entre la segunda y la tercer palabra de triConMenorProb
	parserTestSet->completarOracionConEnMemoria(palabraACompletar, triConMenorProb);


	delete trigramas;

}

string SistemaDelTestSet::buscarMejorTrigramaQueComplete(string triConMenorProb){
	//vector<string> *trigramas = new vector<string>();
	//vector<double> *probas = new vector<double>();

	string A,B;

	boost::char_separator<char> sep(" ");
	boost::tokenizer< boost::char_separator<char> > tokens(triConMenorProb, sep);
	boost::tokenizer< boost::char_separator<char> >::iterator i = tokens.begin();

	A = i.current_token();
	i++;
	B = i.current_token();

	size_t pos_histA = heisenberg->unigramas->buscarUnigrama(A, A.length());
	size_t posBenVocab = heisenberg->unigramas->buscarUnigrama(B, B.length());
	size_t pos = heisenberg->bigramas->buscarBigrama(A, B, pos_histA, posBenVocab);

	size_t posCenVocab = (heisenberg->bigramas->mejor_trigrama_que_lo_completa)[pos];

	return (heisenberg->unigramas->vocab)[posCenVocab];

	/*istream::streampos offsetAB = (heisenberg->bigramas->offsets)[pos];
	if (offsetAB == 0)
		return "";
	parserARPA->imprimirTrigramasQueComienzanConAB(offsetAB, A, B, probas, trigramas);

	int indexTrigramaConMayorProbabilidad = NgramaMasProbable(probas);

	//cout << "TRIGRAMA MAS PROBABLE: --> "+(*trigramas)[trigramaConMayorProbabilidad]+" index-->";
	//cout << trigramaConMayorProbabilidad << endl;

	if (indexTrigramaConMayorProbabilidad == -1)
		return "";

	string trig = (*trigramas)[indexTrigramaConMayorProbabilidad];

	delete trigramas;
	delete probas;

	return tercerPalabraDe(trig);
	*/

}

void SistemaDelTestSet::agregarMejoresTrigramasParaBigramas(){


	for(size_t i = 0; i< heisenberg->bigramas->tam;i++){
		size_t posBenVocab = (heisenberg->bigramas->s_palabras)[i];
		if ( posBenVocab == 0){
			continue;
		}

		vector<string> *trigramas = new vector<string>();
		vector<double> *probas = new vector<double>();

		string A,B,tercer_palabra;

		size_t posAenVocab = (heisenberg->bigramas->hist)[i];

		A = (heisenberg->unigramas->vocab)[posAenVocab];
		B = (heisenberg->unigramas->vocab)[posBenVocab];

		if (B == "</s>"){ // nada puede seguir a eso
			delete trigramas;
			delete probas;
			continue;
		}


		istream::streampos offsetAB = (heisenberg->bigramas->offsets)[i];

		if (offsetAB == 0){ // no deberia pero buen...
			delete trigramas;
			delete probas;
			continue;
		}


		parserARPA->imprimirTrigramasQueComienzanConAB(offsetAB, A, B, probas, trigramas);

		int indexTrigramaConMayorProbabilidad = NgramaMasProbable(probas);

		//cout << "Bigrama: --> "+A+" "+B << endl;
		//cout << "TRIGRAMA MAS PROBABLE: --> "+(*trigramas)[indexTrigramaConMayorProbabilidad]+" index-->";
		//cout << indexTrigramaConMayorProbabilidad << endl;

		if (indexTrigramaConMayorProbabilidad == -1)
			(heisenberg->bigramas->mejor_trigrama_que_lo_completa)[i] = 0;
		else{
			string trig = (*trigramas)[indexTrigramaConMayorProbabilidad];
			tercer_palabra = tercerPalabraDe(trig);
			if (tercer_palabra == ""){
				(heisenberg->bigramas->mejor_trigrama_que_lo_completa)[i] = 0;
				delete trigramas;
				delete probas;
				continue;
			}
			size_t posCenVocab = heisenberg->unigramas->buscarUnigrama(tercer_palabra, tercer_palabra.length());
			(heisenberg->bigramas->mejor_trigrama_que_lo_completa)[i] = posCenVocab;
		}


		delete trigramas;
		delete probas;
	}

	heisenberg->bigramas->offsets.clear();


}

string SistemaDelTestSet::buscarMejorBigramaQueComplete(string triConMenorProb){
	//vector<string> *bigramas = new vector<string>();
	//vector<double> *probas = new vector<double>();

	string A,B;

	boost::char_separator<char> sep(" ");
	boost::tokenizer< boost::char_separator<char> > tokens(triConMenorProb, sep);
	boost::tokenizer< boost::char_separator<char> >::iterator i = tokens.begin();

	A = i.current_token();
	i++;
	B = i.current_token();

	size_t posBenVocab = heisenberg->unigramas->buscarUnigrama(B, B.length());

	size_t pos_completar = heisenberg->unigramas->mejor_bigrama_que_lo_completa[posBenVocab];

	return heisenberg->unigramas->vocab[pos_completar];

	/*istream::streampos offsetB =(heisenberg->unigramas->offsets)[posBenVocab];

	if (offsetB == 0)
		return "";

	parserARPA->imprimirBigramasQueComienzanCon((heisenberg->unigramas->offsets)[posBenVocab], B, probas, bigramas);

	int bigramaConMayorProbabilidad = NgramaMasProbable(probas);

	//cout << "BIGRAMA MAS PROBABLE: --> "+(*bigramas)[bigramaConMayorProbabilidad]+" index-->";
	//cout << bigramaConMayorProbabilidad << endl;

	string big = (*bigramas)[bigramaConMayorProbabilidad];

	delete bigramas;
	delete probas;

	return segundaPalabraDe(big);
*/
}

void SistemaDelTestSet::agregarMejoresBigramasParaUnigramas(){


	for(size_t i = 0; i< heisenberg->unigramas->tam;i++){
		size_t posAenVocab = i;
		string A = (heisenberg->unigramas->vocab)[i];
		if ( A == ""){
			continue;
		}

		vector<string> *bigramas = new vector<string>();
		vector<double> *probas = new vector<double>();

		string segunda_palabra;


		if (A == "</s>"){ // nada puede seguir a eso
			delete bigramas;
			delete probas;
			continue;
		}


		istream::streampos offsetA = (heisenberg->unigramas->offsets)[i];

		if (offsetA == 0){ // no deberia pero buen...
			delete bigramas;
			delete probas;
			continue;
		}


		parserARPA->imprimirBigramasQueComienzanCon(offsetA, A, probas, bigramas);

		int indexBigramaConMayorProbabilidad = NgramaMasProbable(probas);

		//cout << "Bigrama: --> "+A+" "+B << endl;
		//cout << "TRIGRAMA MAS PROBABLE: --> "+(*trigramas)[indexTrigramaConMayorProbabilidad]+" index-->";
		//cout << indexTrigramaConMayorProbabilidad << endl;

		if (indexBigramaConMayorProbabilidad == -1)
			(heisenberg->unigramas->mejor_bigrama_que_lo_completa)[i] = 0;
		else{
			string big = (*bigramas)[indexBigramaConMayorProbabilidad];
			segunda_palabra = segundaPalabraDe(big);
			if (segunda_palabra == ""){
				(heisenberg->unigramas->mejor_bigrama_que_lo_completa)[i] = 0;
				delete bigramas;
				delete probas;
				continue;
			}
			size_t posBenVocab = heisenberg->unigramas->buscarUnigrama(segunda_palabra, segunda_palabra.length());
			(heisenberg->unigramas->mejor_bigrama_que_lo_completa)[i] = posBenVocab;
		}


		delete bigramas;
		delete probas;
	}

	heisenberg->unigramas->offsets.clear();


}

string SistemaDelTestSet::tercerPalabraDe(string trigrama){
	string A,B,C;

	boost::char_separator<char> sep(" ");
	boost::tokenizer< boost::char_separator<char> > tokens(trigrama, sep);
	boost::tokenizer< boost::char_separator<char> >::iterator i = tokens.begin();

	A = i.current_token();
	if (i== tokens.end())
		return "";
	i++;
	B = i.current_token();
	if (i== tokens.end())
		return "";
	i++;
	C = i.current_token();

	//cout << "TERCER PALABRA DE "+trigrama+" es --> "+C <<endl;
	return C;
}

string SistemaDelTestSet::segundaPalabraDe(string bigrama){
	string A,B,C;

	boost::char_separator<char> sep(" ");
	boost::tokenizer< boost::char_separator<char> > tokens(bigrama, sep);
	boost::tokenizer< boost::char_separator<char> >::iterator i = tokens.begin();

	A = i.current_token();
	i++;
	B = i.current_token();

	//cout << "TERCER PALABRA DE "+trigrama+" es --> "+C <<endl;
	return B;
}

void SistemaDelTestSet::pruebas(){


	//cout << heisenberg->unigramas->vocab[heisenberg->unigramas->buscarUnigrama(string("to"), 2)] << endl;

	//parser.imprimirTrigramasQueComienzanConAB((heisenberg->bigramas->offsets)[pos], string("to"), string("reduce"));

	vector<string> *trigramas = new vector<string>();
	vector<double> *probas = new vector<double>();
/*
	cout << heisenberg->bigramas->obtenerProbabilidad(string("He"), string("added"))<<endl;
	cout << heisenberg->trigramas->obtenerProbabilidad(string("<s>"), string("He"), string("added"))<<endl;
	cout << heisenberg->obtenerProb("<s> He added") <<endl;
*/
	string A = "and";
	string B = "the";
	size_t pos_histA = heisenberg->unigramas->buscarUnigrama(A, A.length());
	size_t posBenVocab = heisenberg->unigramas->buscarUnigrama(B, B.length());
	size_t pos = heisenberg->bigramas->buscarBigrama(A, B, pos_histA, posBenVocab);

	parserARPA->imprimirTrigramasQueComienzanConAB((heisenberg->bigramas->offsets)[pos], A, B, probas, trigramas);

	A = "ones";
	B = "the";
	pos_histA = heisenberg->unigramas->buscarUnigrama(A, A.length());
	posBenVocab = heisenberg->unigramas->buscarUnigrama(B, B.length());
	pos = heisenberg->bigramas->buscarBigrama(A, B, pos_histA, posBenVocab);

	cout << "POSICION DE "+A+" "+B+" EN VECTOR DE BIGRAMAS=  --->" << pos << endl;

	parserARPA->imprimirTrigramasQueComienzanConAB((heisenberg->bigramas->offsets)[pos], A, B, probas, trigramas);

	parserARPA->imprimirBigramasQueComienzanCon((heisenberg->unigramas->offsets)[pos_histA],A,probas,trigramas);

	parserARPA->imprimirBigramasQueComienzanCon((heisenberg->unigramas->offsets)[posBenVocab],B,probas,trigramas);

	crearOracionEnBaseA("there","was");
	crearOracionEnBaseA("Argentina", "is");

	crearOracionEnBaseA("bus", "will");

	crearOracionEnBaseA("and");
	crearOracionEnBaseA("Today");


	//heisenberg.bigramas->imprimirBigramas();
}

int SistemaDelTestSet::trigramaMenosProbable(vector<double> *probas){
	double menorProb = 1;
	int index = -1;

	for(unsigned int i = 0; i< probas->size(); i++){
		if ((*probas)[i] < menorProb){
			menorProb =(*probas)[i];
			index = i;
		}
	}
	return index;
}

int SistemaDelTestSet::NgramaMasProbable(vector<double> *probas){
	for(unsigned int i = 0; i< probas->size(); i++)
			(*probas)[i] = pow(10,(*probas)[i]);

	double mayorProb = 0;
	int index = -1;

	for(unsigned int i = 0; i< probas->size(); i++){
		if ((*probas)[i] > mayorProb){
			mayorProb =(*probas)[i];
			index = i;
		}
	}
	return index;
}

void SistemaDelTestSet::crearOracionEnBaseA(string A, string B){


	cout << "ORACION EN BASE A "+A+" "+B << endl;

	vector<string> oracion(10);
	oracion[0] = A;
	oracion[1] = B;
	for(int i=2; i<10 ; i++){
		oracion[i] = buscarMejorTrigramaQueComplete(oracion[i-2]+" "+oracion[i-1]+" "+"bla");
	}

	for(int i=0; i<10 ; i++){
			cout << oracion[i]+" ";
	}
	cout << endl;
}

void SistemaDelTestSet::crearOracionEnBaseA(string A){


	cout << "ORACION EN BASE A "+A+" " << endl;

	vector<string> oracion(10);
	oracion[0] = A;
	for(int i=1; i<10 ; i++){
		oracion[i] = buscarMejorBigramaQueComplete("bla "+oracion[i-1]+" bla");
	}

	for(int i=0; i<10 ; i++){
			cout << oracion[i]+" ";
	}
	cout << endl;
}

void SistemaDelTestSet::imprimirNoPrecide(){
	cout << "NO PREDIJO " << this->noprecide << " FRASEEEEES" << endl;
}
